package name.luoyong.base.ds;

import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;



import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.Transactional;

public abstract class AbstractDao<Entity extends AbstractEntity> {
	
	
	@PersistenceContext
	protected EntityManager em;
	@Autowired
	protected JdbcTemplate jdbcTemplate;
	
	private Class<Entity> EntityClass;
	private String tableName;
	
	public AbstractDao(Class<Entity> clazz) {
		this.EntityClass = clazz;
		this.tableName = clazz.getSimpleName();
	}
	
	public AbstractDao(Class<Entity> clazz, String tableName) {
		this.EntityClass = clazz;
		this.tableName = tableName;
	}

	
	// basic CRUD
	@Transactional
	public void persist(Entity entity) {
		System.out.println("begin AbstractDao.persist() : "+entity);
		em.persist(entity);
		System.out.println("end AbstractDao.persist() : "+entity);
	}
	
	public Entity find(Long id) {
		return em.find(EntityClass, id);
	}
	
	@Transactional
	public void merge(Entity entity) {
		em.merge(entity);
	}
	
	@Transactional
	public void remove(Entity entity) {
		em.remove(entity);
	}
	
	
	// query By Attr  (base jpql)
	public Entity querySingleByAttr(Object... params) {
		System.out.println("begin AbstractDao.querySingleByAttr()");
		Query query = createQueryByAttr(params);
		System.out.println("middle AbstratDao.querySingleByAttr()");
		Entity entity = getSingle(query);
		System.out.println("end AbstratDao.querySingleByAttr()");

		return entity;
	}
	
	public List<Entity> queryListByAttr(Object... params) {
		Query query = createQueryByAttr(params);
		return query.getResultList();
	}
	
	public List<Entity> queryPaginationByAttr( int first, int max, Object... params) {
		Query query = createQueryByAttr(params);
		return pagination(first, max, query);
	}
	
	// query By JPQL
	public Entity querySingleByJPQL(String jpql, Object... params) {
		Query query = createQueryByJPQL(jpql, params);
		return getSingle(query);
	}
	
	public List<Entity> queryListByJPQL(String jpql, Object... params) {
		Query query = createQueryByJPQL(jpql, params);
		return query.getResultList();
	}
	
	public List<Entity> queryPaginationByJPQL(int first, int max, String jpql, Object... params) {
		Query query = createQueryByJPQL(jpql, params);
		return pagination(first, max, query);
	}
	
	// query by jdbcTemplate
	public Map<String, Object> queryMap(String sql, Object... params) {
		try {
			return jdbcTemplate.queryForMap(sql, params);
		} catch (Exception e) {
			return null;
		}
	}
	
	// count
	public int count(Object...params) {
		String sql = "select count(*) from "+this.tableName+" where ";
		Object[] args = new Object[params.length/2];
		for(int i=0; i<params.length; i+=2) {
			sql += params[i]+"=? ";
			args[i/2] = params[i+1];
		}
		System.out.println("sql : "+sql);
		System.out.println("args : "+args);
		for(Object obj : args) {
			System.out.println(obj);
		
		}
		return jdbcTemplate.queryForObject(sql, args, Integer.class);
	}
	
	// basic Methods
	private Query createQueryByAttr(Object... params) {
		if(params.length%2 != 0) {
			try {
				throw new Exception("Error : 参数不是偶数个。\n Error from "+EntityClass);
			} catch (Exception e) {
				e.printStackTrace();
			}
			return null;
		}
		
		String sql = "from "+tableName+" where 1=1 ";
		for(int i=0; i<params.length; i+=2) {
			String attrName = (String)params[i];
			sql+= " and "+attrName+"=:"+attrName;
		}
		
		System.out.println(sql);
		
		Query query = em.createQuery(sql, EntityClass);
		
		for(int i=0; i<params.length; i+=2) {
			String attrName = (String) params[i];
			Object attrValue= params[i+1];
			query.setParameter(attrName, attrValue);
		}
		
		return query;
	}
	
	private Query createQueryByJPQL(String jpql, Object... params) {
		Query query = em.createQuery(jpql);
		setParamsToQuery(query, params);
		return query;
	}
	
	private Query setParamsToQuery(Query query, Object... params) {
		for(int i=0; i<params.length; i++) {
			query.setParameter(i, params[i]);
		}
		return query;
	}
	
	private List pagination( int first, int max, Query query) {
		query.setFirstResult(first);
		query.setMaxResults(max);
		return query.getResultList();
	}
	
	private Entity getSingle(Query query) {
		List<Entity> list = pagination(0,1, query);
		if(list.size() == 1) {
			return list.get(0);
		} else {
			return null;
		}
	}
	
}
